package com.tevintao.marketingsystem.xposted;

import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;

import com.tevintao.marketingsystem.log.LogManager;

import java.util.Arrays;
import java.util.List;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

/**
 * Created by tevintao on 2016/3/31.
 */
public class HookBindServiceManager {

    public static final String BINDER_TRANSACT = "transact";
    public static final String BINDER_EXEC_TRANSACT = "execTransact";


    private static final int BITS_TOKEN = 16;
    private static final int FLAG_ALL = 0xFFFF;
    private static final int MASK_TOKEN = 0xFFFF;

    private static final int PING_TRANSACTION = ('_' << 24) | ('P' << 16) | ('N' << 8) | 'G';
    private static final int DUMP_TRANSACTION = ('_' << 24) | ('D' << 16) | ('M' << 8) | 'P';
    private static final int INTERFACE_TRANSACTION = ('_' << 24) | ('N' << 16) | ('T' << 8) | 'F';
    private static final int TWEET_TRANSACTION = ('_' << 24) | ('T' << 16) | ('W' << 8) | 'T';
    private static final int LIKE_TRANSACTION = ('_' << 24) | ('L' << 16) | ('I' << 8) | 'K';
    private static final int SYSPROPS_TRANSACTION = ('_' << 24) | ('S' << 16) | ('P' << 8) | 'R';


    public static List<String> cServiceDescriptor = Arrays.asList("android.accounts.IAccountManager",
            "android.app.IActivityManager",
            "android.content.IClipboard",
            "android.net.IConnectivityManager",
            "android.content.IContentService",
            "android.location.ILocationManager",
            "com.android.internal.telephony.ITelephonyRegistry",
            "com.android.internal.telephony.ITelephonyRegistryMSim",
            "android.content.pm.IPackageManager",
            "com.android.internal.telephony.IPhoneSubInfo",
            "com.android.internal.telephony.msim.IPhoneSubInfoMSim",
            "android.view.IWindowManager",
            "android.net.wifi.IWifiManager",
            "android.net.sip.ISipService",
            "com.android.internal.telephony.ISms",
            "android.nfc.INfcAdapter",
            "com.android.internal.appwidget.IAppWidgetService",
            "android.bluetooth.IBluetooth",
            "android.bluetooth.IBluetoothManager",
            "android.hardware.input.IInputManager",
            "android.gui.SensorServer",
            "android.hardware.usb.IUsbManager",
            "android.hardware.ICameraService",
            "android.app.IApplicationThread",
            "android.content.IContentProvider",
            "android.view.IWindowSession");

    public static List<String> mRestrictServierName = Arrays.asList(
            "android.accounts.IAccountManager",
//            "android.app.IActivityManager",
//            "android.content.IClipboard",
//            "android.net.IConnectivityManager",
//            "android.content.IContentService",
//            "android.location.ILocationManager",
//            "com.android.internal.telephony.ITelephonyRegistry",
//            "com.android.internal.telephony.ITelephonyRegistryMSim",
//            "android.content.pm.IPackageManager",
//            "com.android.internal.telephony.IPhoneSubInfo",
//            "com.android.internal.telephony.msim.IPhoneSubInfoMSim",
//            "android.view.IWindowManager",
//            "android.net.wifi.IWifiManager",
//            "android.net.sip.ISipService",
//            "com.android.internal.telephony.ISms",
            "android.nfc.INfcAdapter",
//            "com.android.internal.appwidget.IAppWidgetService",
//            "android.bluetooth.IBluetooth",
//            "android.bluetooth.IBluetoothManager",
//            "android.hardware.input.IInputManager",
//            "android.gui.SensorServer",
            "android.hardware.usb.IUsbManager"//,
//            "android.hardware.ICameraService",
//            "android.app.IApplicationThread",
//            "android.content.IContentProvider",
//            "android.view.IWindowSession"
    );

    private static HookBindServiceManager ourInstance = new HookBindServiceManager();

    public static HookBindServiceManager getInstance() {
        return ourInstance;
    }

    private HookBindServiceManager() {
    }


    public void hookBind(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        Class bpCls;
        try {
            bpCls = Class.forName("android.os.BinderProxy");
            HookDeviceModule.hookAllMethodByName(bpCls, BINDER_TRANSACT, mTransactXcHook);
            LogManager.getInstance().logHookBinderInfo("find class bpCls = " + bpCls);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        HookDeviceModule.hookAllMethodByName(Binder.class, BINDER_EXEC_TRANSACT, mExecTransactXcHook);
    }

    private XC_MethodHook mTransactXcHook = new XC_MethodHook() {

        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            // Check interface name
            IBinder binder = (IBinder) param.thisObject;
            String descriptor = (binder == null ? null : binder.getInterfaceDescriptor());

            if (!TextUtils.isEmpty(descriptor) && mRestrictServierName.contains(descriptor)) {
                LogManager.getInstance().logHookBinderInfo("mTransactXcHook  ------------ = " + descriptor + HookDeviceModule.getArgsStr(param.args));
                param.setThrowable(new RemoteException("i am good"));
            } else {
                LogManager.getInstance().logHookBinderInfo("mTransactXcHook ++++++++++++ = " + descriptor + HookDeviceModule.getArgsStr(param.args));
            }
            LogManager.getInstance().logHookBinderInfo("mTransactXcHook = " + param.thisObject + " params = " + HookDeviceModule.getArgsStr(param.args) + " descriptor " + descriptor);
        }
    };

    private XC_MethodHook mExecTransactXcHook = new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            // Check interface name
            IBinder binder = (IBinder) param.thisObject;
            String descriptor = (binder == null ? null : binder.getInterfaceDescriptor());

            if (!TextUtils.isEmpty(descriptor) && mRestrictServierName.contains(descriptor)) {
                LogManager.getInstance().logHookBinderInfo("mExecTransactXcHook  ------------ = " + descriptor + HookDeviceModule.getArgsStr(param.args));
                param.setThrowable(new RemoteException("i am good"));
            } else  {
                LogManager.getInstance().logHookBinderInfo("mExecTransactXcHook ++++++++++++ = " + descriptor + HookDeviceModule.getArgsStr(param.args));
            }
            LogManager.getInstance().logHookBinderInfo("mExecTransactXcHook = " + param.thisObject + " params = " + HookDeviceModule.getArgsStr(param.args) + " descriptor " + descriptor);
        }
    };


    private static boolean isManagementTransaction(int code) {
        return (code == PING_TRANSACTION || code == DUMP_TRANSACTION || code == INTERFACE_TRANSACTION
                || code == TWEET_TRANSACTION || code == LIKE_TRANSACTION || code == SYSPROPS_TRANSACTION);

    }
}
